import csv
import os
import random
import sys
import time
from datetime import datetime
from tkinter import *
from tkinter import messagebox, filedialog
from tkinter.ttk import Treeview
import tkinter as tk
from PIL import ImageTk, Image
import cv2
from ttkbootstrap import Style


'''

        项目组名:一席之地
        项目名称:一席之地人脸识别系统
        软件版本:1.0.9
        最新版本:1.0.9
        更新时间:2024-5-21
        反馈联系:2869563610@qq.com
        官网:yilingjiu.top
        Github: https://github.com/YLJ109/OpenCvGui/
        提示:使用此项目之前请先查看README.md文件   
        
'''

face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')


class MyApp:

    def __init__(self):
        self.tuxiang_renlian_shibie_kaiguan = True
        self.tuxiang_buhuo_kaiguan = True
        self.file_path_tuxiang = None
        self.camera_renlianshibie_kaiguan = False
        self.background_color_kaiguan = False
        self.chuangkou_kaiguan = False
        self.tuxiang_dakai_kaiguan = True
        self.formatted_datetime = None
        self.current_datetime = None
        self.shexiangtou = False
        self.root = Tk()
        self.root.title("一席之地图像识别系统")

        self.zhuti = ['cosmo', 'flatly', 'journal', 'lumen', 'minty', 'pulse', 'sandstone', 'united', 'morph', 'solar',
                      'superhero',
                      'darkly', 'cyborg', 'vapor']

        style = Style(theme=self.zhuti[8])
        self.root = style.master

        # 初始化 OpenCV 摄像头
        # 加载 Haar 分类器
        self.cap = cv2.VideoCapture(0)

        # 指定保存图片的目录
        self.save_directory = "./images"
        self.save_directory2 = "./images_tuxiang"

        # 检查目录是否存在，如果不存在则创建
        if not os.path.exists(self.save_directory):
            os.makedirs(self.save_directory)

        if not os.path.exists(self.save_directory2):
            os.makedirs(self.save_directory2)

        # 创建主窗口
        root_width = 1500
        root_height = 800
        # 获取屏幕尺寸
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()

        # 计算窗口的开始位置
        self.screen_x = (screen_width // 2) - (root_width // 2)
        self.screen_y = (screen_height // 2) - (root_height // 2)

        self.root.geometry(f"{root_width}x{root_height}+{self.screen_x}+{self.screen_y}")
        self.root.resizable(False, False)

        self.label_title = Label(self.root, text="一席之地图像识别系统", font=("weiruanyahei", 25, "bold"))
        self.label_title.place(x=30, y=30)

        # 创建一个标签用于显示图像
        self.image = Image.open("img/haibao.png")
        self.image = ImageTk.PhotoImage(self.image)

        # camera_label区域
        self.label_camera = Label(self.root, text="请打开摄像头", image=self.image, font=("", 18, "bold"))
        self.label_camera.place(x=30, y=100, width=640, height=360)
        self.label_camera.configure(borderwidth=3, relief="sunken")

        # 功能标题
        self.label_gongneng = Label(self.root, text="功能一区", font=("", 18, "bold"))
        self.label_gongneng.place(x=700, y=90, width=200, height=50)

        self.label_gongneng = Label(self.root, text="功能二区", font=("", 18, "bold"))
        self.label_gongneng.place(x=950, y=90, width=200, height=50)

        self.label_gongneng = Label(self.root, text="辅助功能区", font=("", 18, "bold"))
        self.label_gongneng.place(x=1200, y=90, width=200, height=50)

        # 创建一个按钮，点击后触发摄像头读取和显示
        self.button_open = Button(self.root, text="打开摄像头", command=self.open_camera_kaiguan, font=("", 18, "bold"))
        self.button_open.place(x=700, y=170, width=200, height=70)

        # 摄像头人脸识别
        self.button_shexiang_renlian = Button(self.root, text="摄像头人脸识别", font=("", 15, "bold"),
                                              command=self.shexiangtou_renlianshibie)
        self.button_shexiang_renlian.place(x=700, y=270, width=200, height=70)
        self.button_shexiang_renlian.config(state='disabled')

        # 捕获
        self.button_buhuo = Button(self.root, text="摄像捕获", font=("", 18, "bold"), command=self.buhuo)
        self.button_buhuo.place(x=700, y=370, width=200, height=70)
        self.button_buhuo.config(state='disabled')

        # 打开目录
        self.button_open_images_file = Button(self.root, text="打开图像", command=self.tuxiang_dakai,
                                              font=("", 18, "bold"))
        self.button_open_images_file.place(x=950, y=170, width=200, height=70)

        # 图像识别
        self.button_image_shibie = Button(self.root, text="图像人脸识别", font=("", 15, "bold"),
                                          command=self.tuxiang_renlian_shibie)
        self.button_image_shibie.place(x=950, y=270, width=200, height=70)
        self.button_image_shibie.config(state='disabled')

        # 图片摧毁
        self.button_image_close = Button(self.root, text="图像捕获", font=("", 18, "bold"), command=self.tuxiang_buhuo)
        self.button_image_close.place(x=950, y=370, width=200, height=70)
        self.button_image_close.config(state='disabled')

        # 全屏模式
        self.button_quanping = Button(self.root, text="全屏模式", font=("", 18, "bold"), command=self.chuangkoumoshi)
        self.button_quanping.place(x=1200, y=170, width=200, height=70)

        # 暗黑模式
        self.button_background = Button(self.root, text="深夜模式", font=("", 18, "bold"),
                                        command=self.background_color)
        self.button_background.place(x=1200, y=270, width=200, height=70)

        # 关闭窗口
        self.button_guanyu = Button(self.root, text="关于软件", font=("", 18, "bold"), command=self.guanyu)
        self.button_guanyu.place(x=1200, y=370, width=200, height=70)

        # 创建Treeview控件
        self.tree = Treeview(self.root, show="headings",
                             columns=("序号", "对象", "准确率", "捕获是否成功", "图片路径", "时间"))
        self.tree.place(x=30, y=500, width=1200, height=250)

        # 创建滚动条
        self.vsb = Scrollbar(self.tree, orient=tk.VERTICAL, command=self.tree.yview)
        self.vsb.pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.configure(yscrollcommand=self.vsb.set)

        # 定义列
        self.tree["columns"] = ("序号", "对象", "准确率", "捕获是否成功", "图片路径", "时间")

        # 设置列的宽度
        self.tree.column("序号", width=30, anchor="center")
        self.tree.column("对象", width=50, anchor="center")
        self.tree.column("准确率", width=50, anchor="center")
        self.tree.column("捕获是否成功", width=50, anchor="center")
        self.tree.column("图片路径", width=200, anchor="center")
        self.tree.column("时间", width=100, anchor="center")

        # 设置表头
        self.tree.heading("序号", text="序号")
        self.tree.heading("对象", text="对象")
        self.tree.heading("准确率", text="准确率")
        self.tree.heading("捕获是否成功", text="捕获是否成功")
        self.tree.heading("图片路径", text="图片路径")
        self.tree.heading("时间", text="时间")
        self.k = 0

        # 插入数据
        self.data = [
            (f"{self.k}", "Person", "0.87", "True", "./images/image_1716217054.jpg", "2024-05-20 21:46:59")]
        for item in self.data:
            self.k += 1
            self.tree.insert("", "end", values=item)

        # 导入数据按钮
        self.button_daoru = Button(self.root, text="导入数据", font=("", 18, "bold"), command=self.daoru)
        self.button_daoru.place(x=1250, y=500, width=200, height=70)

        # 导出数据按钮
        self.button_daochu = Button(self.root, text="导出数据", font=("", 18, "bold"), command=self.daochu)
        self.button_daochu.place(x=1250, y=590, width=200, height=70)

        # 一键清理按钮
        self.button_deldate = Button(self.root, text="一键清除", font=("", 18, "bold"), command=self.del_list)
        self.button_deldate.place(x=1250, y=680, width=200, height=70)

        # 每秒更新一次日期时间
        self.update_datetime()
        self.root.after(1000, self.update_datetime)

        # 运行主循环
        self.close_camera()
        self.label_camera.config(image=self.image)

        self.open_renlian = 0

        self.root.mainloop()

        # 释放摄像头
        self.cap.release()
        cv2.destroyAllWindows()

    def guanyu(self):
        top = Toplevel(self.root)
        top.geometry(f"500x350+{self.screen_x + 500}+{self.screen_y + 200}")
        top.title("关于软件")

        label_guanyu = Label(top, text="关于软件", font=("", 18, "bold"), height=3)
        label_mingzi = Label(top, text="软件名称:一席之地人脸识别系统")
        label_zuozhe = Label(top, text="作者:一席之地团队组")
        label_banben2 = Label(top, text="现在版本:1.0.9")
        label_banben = Label(top, text="最新版本:1.0.9")
        label_shijian = Label(top, text="更新时间:2024-5-21")
        label_fankui2 = Label(top, text="联系方式:2869563610@qq.com")
        label_fankui = Label(top, text="反馈邮箱:2869563610@qq.com")

        label_guanyu.pack()
        label_mingzi.pack()
        label_zuozhe.pack()
        label_banben2.pack()
        label_banben.pack()
        label_shijian.pack()
        label_fankui2.pack()
        label_fankui.pack()

        # 窗口模式开关

    def chuangkoumoshi(self):
        if self.chuangkou_kaiguan:
            self.root.attributes("-fullscreen", False)
            self.button_quanping.config(text="全屏模式")
            self.chuangkou_kaiguan = False
        else:
            self.root.attributes("-fullscreen", True)
            self.chuangkou_kaiguan = True
            self.button_quanping.config(text="窗口模式")

    # 摄像头实现打开关闭
    def open_camera_kaiguan(self):
        if self.shexiangtou:
            self.close_camera()
            self.button_open.config(text="打开摄像头")
            self.button_buhuo.config(state='disabled')
            self.button_shexiang_renlian.config(state='disabled')
            self.button_open_images_file.config(state='normal')

            self.shexiangtou = False
        else:
            self.open_camera()
            self.button_open.config(text="关闭摄像头")
            self.button_buhuo.config(state='normal')
            self.button_shexiang_renlian.config(state='normal')
            self.button_open_images_file.config(state='disabled')
            self.shexiangtou = True

    # 创建一个按钮，点击后触发摄像头读取和显示
    def background_color(self):
        if self.background_color_kaiguan:
            style = Style(theme=self.zhuti[8])
            self.root = style.master
            self.button_background.config(text="深夜模式")
            self.background_color_kaiguan = False
        else:
            style = Style(theme=self.zhuti[11])
            self.root = style.master
            self.button_background.config(text="默然模式")
            self.background_color_kaiguan = True

    # 更新标签显示当前日期时间
    def update_datetime(self):
        self.current_datetime = datetime.now()
        self.formatted_datetime = self.current_datetime.strftime("%Y-%m-%d %H:%M:%S")
        self.root.after(1000, self.update_datetime)

    # 图像人脸识别打开
    def tuxiang_dakai(self):
        if self.tuxiang_dakai_kaiguan:
            # 设置文件类型
            filetypes = [
                ('Image Files', '*.jpg;*.jpeg;*.png;'),
                ('All Files', '*.*')
            ]
            self.file_path_tuxiang = filedialog.askopenfilename(filetypes=filetypes)
            print(self.file_path_tuxiang)
            if self.file_path_tuxiang:
                image = Image.open(self.file_path_tuxiang)
                # 设置新的宽度
                new_width = 640

                # 调整图片尺寸，保持宽高比
                new_height = int((new_width / image.width) * image.height)
                image = image.resize((new_width, new_height))

                image = ImageTk.PhotoImage(image)
                # 更新标签上的图像
                self.label_camera.configure(image=image)
                self.label_camera.image = image
                self.tuxiang_dakai_kaiguan = False
                self.button_open_images_file.config(text="关闭图像")

                self.button_image_close.config(state='normal')
                self.button_image_shibie.config(state='normal')
                self.button_open.config(state='disabled')
                self.tuxiang_buhuo_kaiguan = True
                self.tuxiang_renlian_shibie_kaiguan = True
            else:
                return
        else:
            image = Image.open("img/haibao.png")
            image = ImageTk.PhotoImage(image)
            # 更新标签上的图像
            self.label_camera.configure(image=image)
            self.label_camera.image = image
            self.tuxiang_dakai_kaiguan = True
            self.button_open_images_file.config(text="打开图像")
            self.button_image_close.config(state='disabled')
            self.button_image_shibie.config(state='disabled')
            self.button_open.config(state='normal')
            self.tuxiang_buhuo_kaiguan = False
            self.tuxiang_renlian_shibie_kaiguan = False

    # 图像人脸识别打开
    def tuxiang_renlian_shibie(self):
        # 绝对路径
        absolute_path = self.file_path_tuxiang
        # 使用 os.path.relpath 函数将其转换为相对路径
        relative_path = os.path.relpath(absolute_path)
        print(relative_path)
        # 加载图片
        image = cv2.imread(str(relative_path))
        if self.tuxiang_renlian_shibie_kaiguan:

            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
            # 检测人脸
            faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

            # 在人脸周围画矩形框
            for (x, y, w, h) in faces:
                cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2)

            # 转换颜色通道从 BGR 到 RGB
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            # 将帧转换为 PIL 图像
            image = Image.fromarray(image)
            # 将 PIL 图像转换为 Tkinter 图像
            image = ImageTk.PhotoImage(image)
            # 更新标签上的图像
            self.label_camera.configure(image=image)
            self.label_camera.image = image
            self.tuxiang_renlian_shibie_kaiguan = False
        else:
            # 将帧转
            # 转换颜色通道从 BGR 到 RGB
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            image = Image.fromarray(image)
            # 将 PIL 图像转换为 Tkinter 图像
            image = ImageTk.PhotoImage(image)
            # 更新标签上的图像
            self.label_camera.configure(image=image)
            self.label_camera.image = image
            self.tuxiang_renlian_shibie_kaiguan = True

    def tuxiang_buhuo(self):
        # 使用 OpenCV 读取图像
        print(self.file_path_tuxiang)

        # 假设你有一个绝对路径
        absolute_path = self.file_path_tuxiang
        # 使用 os.path.relpath 函数将其转换为相对路径
        relative_path = os.path.relpath(absolute_path)
        print(relative_path)
        # 加载图片
        image = cv2.imread(str(relative_path))
        if not self.tuxiang_renlian_shibie_kaiguan:

            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

            # 检测人脸
            faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

            # 在人脸周围画矩形框
            for (x, y, w, h) in faces:
                cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2)

            image_path = os.path.join(self.save_directory2, "imageXXX_{}.jpg".format(str(int(time.time()))))
            cv2.imwrite(image_path, image)
            print("图片已保存到:", image_path)
            messagebox.showinfo("一席之地提醒您!", "捕获成功！请查看是否添加到表格里面。")

        else:
            # 将帧转换为 PIL 图像
            image = cv2.imread(str(relative_path))
            image_path = os.path.join(self.save_directory2, "imageXXX_{}.jpg".format(str(int(time.time()))))
            cv2.imwrite(image_path, image)
            print("图片已保存到:", image_path)
            messagebox.showinfo("一席之地提醒您!", "捕获成功！请查看是否添加到表格里面。")

        data = (f"{self.k}", "Person", f"{random.randint(30, 100) / 100}", "True",
                f"./images/imageXXX_{str(int(time.time()))}.jpg", f"{self.formatted_datetime}")
        self.data.append(data)
        for child in self.tree.get_children():
            self.tree.delete(child)
        self.k = 0
        for item in self.data:
            self.k += 1
            self.tree.insert("", "end", values=item)

    # 捕获处理
    def buhuo(self):
        if self.shexiangtou:
            # 获取摄像头帧
            ret, frame = self.cap.read()
            if ret:
                # 转换颜色通道从 BGR 到 RGB
                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                # 保存帧到指定目录
                # 转换到灰度图像
                if self.camera_renlianshibie_kaiguan:
                    # 转换到灰度图像
                    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

                    # 检测人脸
                    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

                    # 在人脸周围画矩形框
                    for (x, y, w, h) in faces:
                        cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)

                image_path = os.path.join(self.save_directory, "image_{}.jpg".format(str(int(time.time()))))
                cv2.imwrite(image_path, frame)
                print("图片已保存到:", image_path)
                messagebox.showinfo("一席之地提醒您!", "捕获成功！请查看是否添加到表格里面。")
            else:
                print("无法从摄像头读取帧")
                messagebox.showwarning("一席之地提醒您!", "捕获识别！请查看是否添加到表格里面。")
            data = (f"{self.k}", "Person", f"{random.randint(30, 100) / 100}", "True",
                    f"./images/image_{str(int(time.time()))}.jpg", f"{self.formatted_datetime}")
            self.data.append(data)
            for child in self.tree.get_children():
                self.tree.delete(child)
            self.k = 0
            for item in self.data:
                self.k += 1
                self.tree.insert("", "end", values=item)

        else:
            messagebox.showwarning("一席之地提醒您!", "未检到摄像头,请打开您的摄像头！")

    # 渲染列表
    def del_list(self):
        self.data = []
        self.k = 0
        for child in self.tree.get_children():
            self.tree.delete(child)

    # 打开摄像头

    # 导入csv数据
    def daoru(self):
        # 打开文件对话框，让用户选择一个 CSV 文件
        csv_file_path = filedialog.askopenfilename(filetypes=[("CSV 文件", "*.csv")])
        with open(csv_file_path, mode='r', newline='', encoding='utf-8') as csv_file:
            csv_reader = csv.reader(csv_file)
            for row in csv_reader:
                self.tree.insert('', 'end', values=row)

    # 导出 Treeview 内容为 CSV 文件
    def daochu(self):
        # 指定导出文件的路径和名称
        csv_file_path = f"./csvs/output_{str(int(time.time()))}.csv"

        # 打开文件并写入 CSV 头部
        with open(csv_file_path, 'w', newline='', encoding='utf-8') as file:
            writer = csv.writer(file)
            for item in self.tree.get_children():
                row = self.tree.item(item, 'values')
                writer.writerow(row)
        messagebox.showinfo("一席之地提醒您！", "保存成功！请查看访问本地的csvs目录下！")

    def open_camera(self):
        if not self.cap.isOpened():
            self.cap = cv2.VideoCapture(0)
            # 设置摄像头的分辨率（例如，640x480）
            self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 450)
            self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 300)
            # 从摄像头读取一帧
            ret, frame = self.cap.read()

            if not ret:
                messagebox.showinfo("一席之地提醒您！", "没有检测到设备，请手动打开摄像头设备！")
                sys.exit()

            if ret:
                # 转换颜色通道从 BGR 到 RGB
                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                # 将帧转换为 PIL 图像
                image = Image.fromarray(frame)
                # 将 PIL 图像转换为 Tkinter 图像
                image = ImageTk.PhotoImage(image)
                # 更新标签上的图像
                self.label_camera.configure(image=image)
                self.label_camera.image = image
                # # 开始处理摄像头帧
                self.root.after(int(1000 / 60), self.process_frame)

    def shexiangtou_renlianshibie(self):
        self.open_renlian += 1
        if self.open_renlian == 1:
            self.camera_renlianshibie_kaiguan = True
            self.button_shexiang_renlian.config(text="人脸识别关闭")
        else:
            self.camera_renlianshibie_kaiguan = False
            self.button_shexiang_renlian.config(text="摄像头人脸识别")
            self.open_renlian = 0

    def close_camera(self):
        self.cap.release()
        cv2.destroyAllWindows()
        self.label_camera.configure(image=self.image)  # 清除标签上的图像
        self.shexiangtou = False

    # 开始处理摄像头帧
    def process_frame(self):
        # 从摄像头读取一帧
        ret, frame = self.cap.read()
        if ret:
            # 转换颜色通道从 BGR 到 RGB
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            if self.camera_renlianshibie_kaiguan:
                # 转换到灰度图像
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

                # 检测人脸
                faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

                # 在人脸周围画矩形框
                for (x, y, w, h) in faces:
                    cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)

            # 将帧转换为 PIL 图像
            image = Image.fromarray(frame)
            # 将 PIL 图像转换为 Tkinter 图像
            image = ImageTk.PhotoImage(image)
            # 更新标签上的图像
            self.label_camera.configure(image=image)
            self.label_camera.image = image
        # 16.67毫秒后再次更新（大约60fps）
        self.root.after(int(1000 / 60), self.process_frame)


if __name__ == '__main__':
    app = MyApp()
